package org.infodavid.common.reflect;

import java.lang.reflect.Constructor;
import java.util.Collection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.infodavid.common.reflect.impl.PropertyInstantiationHelper;

/**
 * The Class InstantiationHelper.
 */
public abstract class InstantiationHelper {

	/** The Constant log. */
	private static final Log LOGGER = LogFactory.getLog(InstantiationHelper.class);// NOPMD

	/** The singleton. */
	private static InstantiationHelper singleton;

	static {
		try {
			final Constructor<PropertyInstantiationHelper> cons =
			    PropertyInstantiationHelper.class.getDeclaredConstructor();

			cons.setAccessible(true);
			singleton = cons.newInstance();
		}
		catch (final Throwable e) {
			LOGGER.fatal("Can not instantiate the concrete " + InstantiationHelper.class.getName(), e);

			throw new Error("Can not instantiate the concrete " + InstantiationHelper.class.getName(), e);
		}
	}

	/**
	 * returns the singleton.
	 * @return the singleton
	 */
	public static InstantiationHelper getInstance() {
		return singleton;
	}

	/**
	 * Instantiates a new reflect utility.
	 */
	protected InstantiationHelper() {
		super();
	}

	/**
	 * New instance.
	 * @param <T> the type
	 * @param reference the reference
	 * @return the T
	 * @throws Exception the exception
	 */
	public abstract <T> T newInstance(final Class<T> reference) throws Exception;

	/**
	 * New instance.
	 * @param <T> the type
	 * @param reference the reference
	 * @return the instances
	 * @throws Exception the exception
	 */
	public abstract <T> Collection<T> newInstances(final Class<T> reference) throws Exception;

	/**
	 * Unregister.
	 * @param clazz the class
	 */
	public abstract void unregister(Class<?> clazz);

	/**
	 * Register. This mechanism can be use to define dynamically mock objects.
	 * @param clazz the class
	 * @param impl the implementation to use
	 */
	public abstract void register(Class<?> clazz, Object impl);
}
